package com.ethink.framework.common.web.interceptor;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.ethink.framework.common.constant.CommonConstant;
import com.ethink.framework.common.util.JacksonUtils;
import com.ethink.framework.common.util.PathMatcherUtil;
import com.ethink.framework.common.web.context.RequestContext;
import com.ethink.framework.common.web.context.SPRequestContextHolder;
import com.ethink.framework.log.constant.LogConstant;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.util.*;

/**
 * http拦截器，解析请求数据
 *
 * @author allen
 * @description
 */
@Slf4j
@Component
public class CustomInterceptor implements HandlerInterceptor {

    private static ObjectMapper objectMapper = new ObjectMapper();

    private static final List<String> NOT_LOG_URLS = Lists.newArrayList(
            "/swagger-ui/**", "/*/v3/api-docs/**", "/v3/api-docs/**"
    );

    private static final List<String> IGNORE_HEARS = Lists.newArrayList(
            "sec-fetch-site", "sec-ch-ua-mobile", "sec-fetch-mode", "sec-ch-ua", "sec-ch-ua-platform","sec-fetch-dest",
            "accept-language", "accept", "accept-encoding","content-length","content-type",
            "connection", "user-agent", "x-forwarded-port","x-forwarded-proto","x-forwarded-for","forwarded"
    );

    /**
     * 执行Controller 前，先执行该拦截。
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        RequestContext requestContext = buildRequestContext(request);
        SPRequestContextHolder.setRequestContext(requestContext);
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
        SPRequestContextHolder.clear();
    }

    /**
     * 封装 RequestContext
     *
     * @param httpServletRequest
     * @return
     */
    private RequestContext buildRequestContext(HttpServletRequest httpServletRequest) throws UnsupportedEncodingException {
        String traceId = httpServletRequest.getHeader(LogConstant.TRACE_ID_HEADER);
        if (StringUtils.isEmpty(traceId)) {
            traceId = IdUtil.getSnowflakeNextIdStr();
        }
        MDC.put(LogConstant.LOG_TRACE_ID, traceId);
        Map<String, String> headers = new HashMap<>(64);
        Map<String, String> bizParamMap = new HashMap<>(64);
        Enumeration<String> names = httpServletRequest.getHeaderNames();
        while (names.hasMoreElements()) {
            String name = names.nextElement();
            String value = httpServletRequest.getHeader(name);
            if (name.startsWith(CommonConstant.RequestHeader.HEADER_PREFIX)) {
                bizParamMap.put(name, URLUtil.decode(value));
            } else {
                if (!IGNORE_HEARS.contains(name)) {
                    headers.put(name, value);
                }
            }
        }
        String ip = ServletUtil.getClientIP(httpServletRequest);
        RequestContext requestContext = new RequestContext();
        requestContext.setIp(ip);
        requestContext.setUrl(httpServletRequest.getRequestURI());
        requestContext.setRequestTime(new Date());
        requestContext.setSessionId(httpServletRequest.getRequestedSessionId());
        requestContext.setMethod(httpServletRequest.getMethod());
        requestContext.setHttpHeads(headers);
        requestContext.setRequestParams(httpServletRequest.getParameterMap());
        requestContext.setTraceId(StringUtils.isBlank(traceId) ? IdUtil.fastSimpleUUID() : traceId);
        requestContext.setBizParamMap(bizParamMap);
        if (PathMatcherUtil.notMatch(NOT_LOG_URLS, requestContext.getUrl())) {
            log.info("web_request:{}", JacksonUtils.toJson(requestContext));
        }
        return requestContext;
    }

}